Skip to content

feat(nuxi): use eve framework#2280

Open
HugoRCD wants to merge 23 commits into
mainfrom
feat/nuxi-on-eve
Open

feat(nuxi): use eve framework#2280
HugoRCD wants to merge 23 commits into
mainfrom
feat/nuxi-on-eve

Conversation

@HugoRCD

@HugoRCD HugoRCD commented Jun 23, 2026

Copy link
Copy Markdown
Member

🔗 Linked issue

📚 Description

@HugoRCD HugoRCD self-assigned this Jun 23, 2026
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nuxt Ready Ready Preview, Comment Jun 26, 2026 2:14pm

@socket-security

socket-security Bot commented Jun 23, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @emnapi/runtime is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxthub/core@0.10.7npm/@nuxt/scripts@1.2.1npm/nuxt@4.4.8npm/@nuxt/eslint@1.16.0npm/nuxt-og-image@6.6.0npm/@nuxt/hints@1.1.3npm/evlog@2.19.2npm/eve@0.15.1npm/@nuxt/image@2.0.0npm/@emnapi/runtime@1.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@emnapi/runtime@1.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @internationalized/date is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxt/ui@4.9.0npm/@internationalized/date@3.12.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@internationalized/date@3.12.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @typescript-eslint/eslint-plugin is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxt/eslint@1.16.0npm/@typescript-eslint/eslint-plugin@8.62.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@typescript-eslint/eslint-plugin@8.62.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm better-sqlite3 is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package.jsonnpm/better-sqlite3@12.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/better-sqlite3@12.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm env-runner is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/eve@0.15.1npm/env-runner@0.1.14

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/env-runner@0.1.14. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm eslint-plugin-jsdoc is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxt/eslint@1.16.0npm/eslint-plugin-jsdoc@63.0.7

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/eslint-plugin-jsdoc@63.0.7. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm happy-dom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package.jsonnpm/happy-dom@20.10.6

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/happy-dom@20.10.6. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm ioredis is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxthub/core@0.10.7npm/@nuxt/scripts@1.2.1npm/nuxt@4.4.8npm/nuxt-og-image@6.6.0npm/@nuxt/hints@1.1.3npm/@nuxt/ui@4.9.0npm/evlog@2.19.2npm/eve@0.15.1npm/@nuxt/image@2.0.0npm/ioredis@5.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ioredis@5.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm js-yaml is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxt/content@3.14.0npm/@comark/nuxt@0.4.0npm/@nuxt/eslint@1.16.0npm/capture-website@5.1.0npm/js-yaml@4.2.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/js-yaml@4.2.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm markdown-it is 91.0% likely obfuscated

Confidence: 0.91

Location: Package overview

From: pnpm-lock.yamlnpm/nuxt-content-twoslash@0.4.0npm/markdown-it@14.2.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/markdown-it@14.2.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm web-vitals is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@nuxt/hints@1.1.3npm/web-vitals@5.3.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/web-vitals@5.3.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm yargs is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/capture-website@5.1.0npm/yargs@17.7.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@17.7.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

The Vercel build fails when crawlLinks discovers thousands of routes
concurrently and a payload request returns 500 (e.g. directory-structure/pages).
Matches perf/prerender-no-crawl (#2286).
@HugoRCD HugoRCD marked this pull request as ready for review June 26, 2026 12:14
@HugoRCD HugoRCD requested a review from atinux as a code owner June 26, 2026 12:14
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a0763ab-bada-43b5-96a5-f47541829bd6

📥 Commits

Reviewing files that changed from the base of the PR and between 053dc99 and 8ed3f01.

📒 Files selected for processing (2)
  • layers/nuxi/app/composables/useNuxtAgent.ts
  • layers/nuxi/server/utils/message-parts.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • layers/nuxi/server/utils/message-parts.ts

📝 Walkthrough

Walkthrough

The PR adds a Nuxi Eve layer with new environment and deployment configuration, chat state schema and migrations, public and internal chat APIs, Eve runtime and tool wiring, and client-side chat/session UI updates. It also updates admin MCP prompts and tool outputs to focus on saved web chats, votes, and Vercel observability.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is mostly a template with placeholders and no substantive project details. Replace the template text with a brief summary of the actual Eve migration, key behavior changes, and any testing or issue references.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: moving nuxi to the Eve framework.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/nuxi-on-eve

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (12)
server/mcp/prompts/admin/review-agent-chats.ts-22-22 (1)

22-22: 🎯 Functional Correctness | 🟡 Minor

Chat link path is inconsistent with the actual application routes.

The prompt in server/mcp/prompts/admin/review-agent-chats.ts instructs reports to link to https://nuxt.com/admin/agent/<chat-id>. However, the application only defines the chat route at layers/nuxi/app/pages/dashboard/chat/[id].vue, which corresponds to https://nuxt.com/dashboard/chat/<id>. The /admin/agent/ path does not exist in the codebase, causing generated links to point to non-existent pages and creating a mismatch with the URLs emitted by admin tools.

Update the template to use https://nuxt.com/dashboard/chat/${chat-id}.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/mcp/prompts/admin/review-agent-chats.ts` at line 22, The chat link in
the admin review prompt uses a non-existent `/admin/agent/` route, so update the
template in review-agent-chats.ts to match the real chat page route used by the
app. Change the generated link text for “Worst sessions” to point to the
dashboard chat path, using the same chat id placeholder, so reports align with
the route handled by the chat page component.
nuxt.config.ts-127-128 (1)

127-128: 🔒 Security & Privacy | 🟡 Minor

Remove unused runtimeConfig.internalApiSecret or align checks to use it.

The internalApiSecret key defined in nuxt.config.ts (line 128) is never consumed via useRuntimeConfig().internalApiSecret anywhere in the codebase. The validation logic in layers/nuxi/agent/lib/internal-api.ts and layers/nuxi/server/utils/internal-api.ts reads process.env.INTERNAL_API_SECRET directly.

This creates a redundant (dead) config entry. If future refactoring assumes this value is available via runtimeConfig, it will default to '' (per line 128), bypassing the strict env check.

Action:

  • Option A (Cleanup): Remove internalApiSecret from nuxt.config.ts entirely, as the checks correctly use process.env.
  • Option B (Refactor): Update internal-api.ts files to use const secret = useRuntimeConfig().internalApiSecret (and remove the || '' fallback in nuxt.config.ts to force failure on missing env), centralizing config access.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nuxt.config.ts` around lines 127 - 128, The runtimeConfig entry for
internalApiSecret is redundant because the internal API checks already read
process.env.INTERNAL_API_SECRET directly. Either remove internalApiSecret from
nuxt.config.ts if you want to keep the current env-based validation in
layers/nuxi/agent/lib/internal-api.ts and
layers/nuxi/server/utils/internal-api.ts, or refactor those internal-api helpers
to read useRuntimeConfig().internalApiSecret and drop the fallback so missing
secrets fail consistently.
layers/nuxi/server/api/chats/[id]/state.patch.ts-54-63 (1)

54-63: 🗄️ Data Integrity & Integration | 🟡 Minor

Confirm messages represents the complete history or enforce a full-snapshot contract.

The PATCH handler deletes all existing messages for the chat before inserting body.messages. If the caller passes a partial list, the unlisted messages are permanently lost.

Verification of thread-state.ts confirms the primary caller (createEveFinishHandler) passes the full agentMessages array. However, persistChatState is an exported utility accepting an optional messages array; if invoked elsewhere with a truncated list, data loss will occur.

Additionally, the insert lacks onConflictDoNothing. A payload containing duplicate ids will abort the transaction with a 500 error.

Code context
diff --git a/layers/nuxi/server/api/chats/[id]/state.patch.ts
--- a/layers/nuxi/server/api/chats/[id]/state.patch.ts
+++ b/layers/nuxi/server/api/chats/[id]/state.patch.ts
@@ -54,6 +54,7 @@
     if (body.messages?.length) {
       await tx.delete(schema.messages).where(eq(schema.messages.chatId, id))
       await tx.insert(schema.messages).values(body.messages.map(m => ({
+        // Warning: this deletes all previous messages if body.messages is not a full snapshot
         id: m.id,
         chatId: id,
         role: m.role,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/server/api/chats/`[id]/state.patch.ts around lines 54 - 63, The
PATCH handler in state.patch.ts currently treats body.messages as a full
replacement but does not enforce that contract, so partial payloads can delete
existing chat history; either validate that the caller always provides the
complete snapshot in persistChatState/createEveFinishHandler or change the
update flow to merge instead of replacing. Also update the schema.messages
insert path to tolerate duplicate message ids by using the existing
tx.insert(schema.messages) flow with conflict handling such as
onConflictDoNothing, so a repeated id does not fail the transaction.
layers/nuxi/server/api/internal/github/search-issues.post.ts-6-10 (1)

6-10: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Constrain query to a non-empty string.

query: z.string() accepts an empty string, which forwards a meaningless search to searchGitHubIssues. The sibling web-search endpoint already uses z.string().min(1); align here for consistent input validation.

♻️ Proposed change
   const body = await readValidatedBody(event, z.object({
-    query: z.string(),
+    query: z.string().min(1),
     repo: z.string().optional(),
     state: z.enum(['open', 'closed', 'all']).optional()
   }).parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/server/api/internal/github/search-issues.post.ts` around lines 6
- 10, The request validation in readValidatedBody for search-issues.post.ts
allows an empty query, which can lead to meaningless searches in
searchGitHubIssues. Update the z.object schema used in this handler so the query
field requires a non-empty string, matching the validation pattern used by the
sibling web-search endpoint and keeping input handling consistent.
layers/nuxi/server/api/internal/agent/web-search.post.ts-19-23 (1)

19-23: 🔒 Security & Privacy | 🟡 Minor

Avoid surfacing raw provider error messages.

Returning (error as Error).message directly exposes potential internal provider details and assumes the thrown value is always an Error instance. Instead, log the full error (including the cause chain) server-side and return a generic message to the client.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/server/api/internal/agent/web-search.post.ts` around lines 19 -
23, The web-search error handling in webSearch.post currently returns the raw
provider message to the client and assumes the thrown value is always an Error.
Update the catch block in the web-search handler to log the full error
server-side, including any cause chain, and return a generic client-facing
message instead of using error.message directly.
layers/nuxi/server/utils/internal-api.ts-13-19 (1)

13-19: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Use a constant-time comparison for the bearer secret.

requireInternalRequest is the central auth gate for every internal endpoint, but the secret is compared with !==, which short-circuits on the first differing byte and is susceptible to timing analysis. Prefer a constant-time comparison.

🔒 Proposed hardening
+import { timingSafeEqual } from 'node:crypto'
 import type { H3Event } from 'h3'

 export function requireInternalRequest(event: H3Event) {
   const secret = process.env.INTERNAL_API_SECRET?.trim()

   if (!secret) {
     throw createError({
       statusCode: 503,
       statusMessage: 'Internal API is not configured'
     })
   }

   const authorization = getRequestHeader(event, 'authorization')
-  if (authorization !== `Bearer ${secret}`) {
+  const expected = `Bearer ${secret}`
+  const provided = authorization ?? ''
+  const a = Buffer.from(provided)
+  const b = Buffer.from(expected)
+  if (a.length !== b.length || !timingSafeEqual(a, b)) {
     throw createError({
       statusCode: 401,
       statusMessage: 'Unauthorized'
     })
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/server/utils/internal-api.ts` around lines 13 - 19, The bearer
secret check in requireInternalRequest currently uses a normal string
comparison, which can leak timing information. Update the authorization
validation in this function to use a constant-time equality check when comparing
the incoming Authorization header against the expected Bearer value, while
preserving the existing 401 Unauthorized behavior for mismatches.
layers/nuxi/agent/tools/show_blog_post.ts-6-10 (1)

6-10: 🎯 Functional Correctness | 🟡 Minor

Reject whitespace-only blog titles.

The title field in inputSchema uses z.string() without trim or minimum-length validation. This allows empty or whitespace-only strings to be sent to /api/internal/content, resulting in invalid or unhelpful API queries. Update the schema to trim input and enforce at least one character.

Snippet
inputSchema: z.object({
  title: z.string().describe('The blog post title or search keyword (e.g., "v4", "Nuxt 3.15", "TypeScript")')
}),

Use .trim().min(1) to ensure valid input:

title: z.string().trim().min(1).describe('...')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/agent/tools/show_blog_post.ts` around lines 6 - 10, The title
validation in showBlogPost is too permissive because inputSchema accepts empty
or whitespace-only strings. Update the zod schema for title to trim the value
and require at least one character before it is sent to /api/internal/content,
using the existing inputSchema in show_blog_post.ts so the body builder only
receives valid blog search terms.
layers/nuxi/agent/tools/web_search.ts-6-8 (1)

6-8: 🚀 Performance & Scalability | 🟡 Minor

Require a non-empty, trimmed web search query.

The current inputSchema accepts empty or whitespace-only strings via z.string(). This risks triggering meaningless remote search requests and consuming external search budget.

Update the schema to enforce validation:

Proposed change for layers/nuxi/agent/tools/web_search.ts
inputSchema: z.object({
  query: z.string().min(1, 'Search query cannot be empty').trim().describe('Search query — match the user\'s wording; do not add calendar years unless they asked for one')
}),

Add .min(1) to reject empty strings and .trim() to reject whitespace-only inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/agent/tools/web_search.ts` around lines 6 - 8, The web search
tool schema currently accepts empty or whitespace-only queries because the
`inputSchema` in `web_search.ts` only uses `z.string()`. Update the `query`
field validation in `inputSchema` to require a non-empty trimmed string by
adding trimming and a minimum-length check so invalid searches are rejected
before reaching the remote search call.
layers/nuxi/agent/tools/search_github_issues.ts-6-10 (1)

6-10: 🎯 Functional Correctness | 🟡 Minor

Reject blank issue searches.

The query field currently accepts empty strings and whitespace-only input because z.string() has no length constraints. This allows the agent to trigger broad or invalid GitHub searches. Update the schema to enforce a non-empty, trimmed string:

- query: z.string().describe('Error message, keyword, or search term'),
+ query: z.string().trim().min(1, 'Search query cannot be empty').describe('Error message, keyword, or search term'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/agent/tools/search_github_issues.ts` around lines 6 - 10, The
search_github_issues tool currently allows blank or whitespace-only queries
because inputSchema uses a plain z.string() for query. Update the query
validator in search_github_issues.ts to require a trimmed, non-empty string so
the tool rejects empty searches before reaching GitHub. Use the inputSchema
definition and its query field as the place to apply the constraint.
layers/nuxi/agent/hooks/chat-title.ts-14-29 (1)

14-29: 🩺 Stability & Availability | 🟡 Minor

Wrap the title fetch to prevent network errors from disrupting message flow.

According to the Eve framework documentation, errors thrown from message.received hooks propagate through the event loop and can escalate to turn.failed or session.failed events. The current implementation only handles non-OK HTTP responses but lacks a try/catch wrapper around the fetch call, meaning transient network failures (DNS issues, connection timeouts, etc.) will disrupt the normal message flow instead of degrading gracefully.

Wrap the entire request block in try/catch to ensure title generation remains best-effort:

Proposed fix
-      const response = await fetch(`${appOrigin()}/api/internal/chats/${encodeURIComponent(chatId)}/title`, {
-        method: 'POST',
-        headers: internalHeaders(),
-        body: JSON.stringify({
-          userId: auth.principalId,
-          message: {
-            role: 'user',
-            parts: [{ type: 'text', text: message }]
-          }
-        })
-      })
-
-      if (!response.ok) {
-        const text = await response.text().catch(() => '')
-        console.warn(`[chat-title] title generation failed (${response.status}): ${text}`)
-      }
+      try {
+        const response = await fetch(`${appOrigin()}/api/internal/chats/${encodeURIComponent(chatId)}/title`, {
+          method: 'POST',
+          headers: internalHeaders(),
+          body: JSON.stringify({
+            userId: auth.principalId,
+            message: {
+              role: 'user',
+              parts: [{ type: 'text', text: message }]
+            }
+          })
+        })
+
+        if (!response.ok) {
+          const text = await response.text().catch(() => '')
+          console.warn(`[chat-title] title generation failed (${response.status}): ${text}`)
+        }
+      } catch (error) {
+        console.warn('[chat-title] title generation request errored', error)
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/agent/hooks/chat-title.ts` around lines 14 - 29, The chat title
request in the `message.received` hook only handles non-OK responses, so network
exceptions from `fetch` can still bubble up and break message flow. Wrap the
entire title-generation block in a `try/catch` in `chat-title.ts`, keeping the
existing `response.ok` handling inside the `try` and logging any thrown error as
a best-effort failure. Use the existing `appOrigin()`, `internalHeaders()`, and
`console.warn` path so the hook degrades gracefully without interrupting the
main message lifecycle.
layers/nuxi/app/composables/useAgentChat.ts-197-212 (1)

197-212: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Re-entrancy can double-persist the first user message.

initialDbPersistDone.value = true is set only after the awaited appendUserMessageToChat/createChatWithMessage call. Unlike useStartChat, the chat-mode send/onSubmit path has no loading guard, so two quick submits (or programmatic askQuestion) can both pass the initialDbPersistDone check before either resolves, issuing duplicate persistence calls (and a duplicate POST /api/chats that can error). Set the flag optimistically before awaiting and roll back on failure.

🛡️ Proposed guard-before-await
 async function persistFirstUserMessage(parts: UIMessage['parts']) {
   if (initialDbPersistDone.value) return
+  initialDbPersistDone.value = true

   const metadata = {
     createdAt: new Date().toISOString(),
     ...(useContext.value && agent.currentPage.value ? { pagePath: agent.currentPage.value } : {})
   }

-  if (chatOptions.persistedInDb) {
-    await appendUserMessageToChat(chatOptions.chatId, parts, metadata)
-  } else {
-    await createChatWithMessage(chatOptions.chatId, parts, metadata)
-  }
-
-  initialDbPersistDone.value = true
+  try {
+    if (chatOptions.persistedInDb) {
+      await appendUserMessageToChat(chatOptions.chatId, parts, metadata)
+    } else {
+      await createChatWithMessage(chatOptions.chatId, parts, metadata)
+    }
+  } catch (error) {
+    initialDbPersistDone.value = false
+    throw error
+  }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/app/composables/useAgentChat.ts` around lines 197 - 212, The
first-user-message persistence flow in persistFirstUserMessage can run twice
because initialDbPersistDone.value is only set after awaiting
appendUserMessageToChat/createChatWithMessage. Set the flag optimistically
before the await so concurrent send/onSubmit or askQuestion calls in
useAgentChat cannot pass the guard twice, and if the persistence call fails,
roll the flag back so the message can be retried safely.
layers/nuxi/app/composables/useAgentChat.ts-14-19 (1)

14-19: 📐 Maintainability & Code Quality | 🟡 Minor

Add explicit import for ChatEveState type.

ChatEveState is referenced on line 18 but has no explicit import in this file. The type is defined in layers/nuxi/shared/types/chat.ts, which is not in Nuxt's default auto-import paths (composables/, components/, utils/, types/).

Recommended fix:

import type { ChatEveState } from '../../shared/types/chat'

This ensures the type resolves correctly during compilation, regardless of whether custom modules configure auto-imports for the shared/ directory.

Current imports in useAgentChat.ts
import type { UIMessage } from 'ai'
import { buildMessageParts, getMessageTextLength } from '../../shared/utils/paste-attachment'
import { createEveChatSession } from './eve/session'
import { getOrCreateEveAgent } from './eve/init'
import { toUIMessages } from './eve/adapter'
import {
  createEveFinishHandler,
  readAnonymousTitle,
  resumeOptionsFromChat
} from './eve/thread-state'
import { useChatVotes } from './useChatVotes'
import { usePasteAttachment } from './usePasteAttachment'

Note that eve/thread-state.ts properly imports ChatEveState:

import type { ChatEveState, ChatDetail } from '../../../shared/types/chat'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/app/composables/useAgentChat.ts` around lines 14 - 19,
`useAgentChat.ts` references `ChatEveState` in `ChatModeOptions` without
importing it explicitly, so add a type-only import for `ChatEveState` from the
shared chat types alongside the existing imports. Keep the change localized to
the `useAgentChat` composable and ensure the `ChatModeOptions` definition
resolves the type directly rather than relying on auto-imports.
🧹 Nitpick comments (2)
layers/nuxi/README.md (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Suppress false-positive markdownlint warning.

The fenced code block at line 5 contains a directory tree diagram, not executable code. Add a language tag (e.g., text) to suppress the MD040 warning.

-```
+```text
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/README.md` at line 5, The README’s fenced directory tree block is
triggering a markdownlint MD040 false positive because it has no language tag.
Update the fenced block in the README to use a non-code label such as text so
the directory tree remains rendered as plain text while suppressing the warning.
layers/nuxi/app/composables/useChatTools.ts (1)

85-95: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant double normalization (non-blocking).

getToolText (Line 210) passes a normalized name into parseToolName, which normalizes again internally (Line 86). It's idempotent so behavior is unaffected; consider normalizing in one place to avoid confusion.

Also applies to: 208-210

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/app/composables/useChatTools.ts` around lines 85 - 95,
`getToolText` is normalizing the tool name before calling `parseToolName`, but
`parseToolName` also normalizes internally, causing redundant double
normalization. Update the flow so normalization happens in only one place:
either pass the raw tool name into `parseToolName` and keep its current
behavior, or remove the internal normalization and let `getToolText` own it. Use
the `getToolText` and `parseToolName` symbols to keep the change localized and
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@layers/nuxi/agent/channels/eve.ts`:
- Around line 62-78: The prior-chat context lookup in eve.ts should be tied to
the authenticated user instead of trusting x-nuxi-chat-id alone. Update the
logic around the chatId/session check and the fetch to
/api/internal/chats/.../context so the current user session is forwarded (or
otherwise available) and the internal endpoint verifies chat ownership before
returning a summary. Keep the context-push flow in place, but only load data
when ownership is confirmed by the context endpoint.

In `@layers/nuxi/agent/lib/internal-api.ts`:
- Around line 30-37: The internalFetch helper currently calls fetch without any
AbortSignal, so stalled internal API requests can hang indefinitely. Update
internalFetch in internal-api.ts to always apply a mandatory global timeout (for
example, using AbortSignal.timeout(10000)) on the fetch call, and make sure the
signal is passed alongside the existing init options and headers so every
internal request fails fast instead of blocking the agent turn.

In `@layers/nuxi/agent/tools/admin-mcp.ts`:
- Around line 18-22: The Slack branch in canAccessAdminMcp is too broad because
any Slack issuer or presence of team_id currently grants admin MCP access.
Update canAccessAdminMcp to only allow Slack sessions when the team_id matches
an explicit allowlist, while keeping the existing authAttr(..., 'role') ===
'admin' path for non-Slack web/admin sessions. Use the existing
canAccessAdminMcp and authAttr helpers to locate and tighten the Slack access
check.

In `@layers/nuxi/agent/tools/show_module.ts`:
- Around line 28-31: The module lookup in show_module.ts currently uses a bare
fetch call without any application-level timeout, which can leave the tool
hanging on slow api.nuxt.com responses. Update the request logic around the
module API call in the show_module function to use the project’s existing
timeout wrapper or an AbortController-based timeout, and make sure timeout
failures are handled consistently with the existing 404 and non-ok response
handling.

In `@layers/nuxi/app/composables/eve/init.ts`:
- Around line 4-36: The module-scoped agentsByChatId cache in
getOrCreateEveAgent can leak across SSR requests and grow indefinitely on the
client. Update the caching in layers/nuxi/app/composables/eve/init.ts so it does
not persist globally on the server (use import.meta.server/client gating or
request-scoped state) and ensure entries are cleaned up automatically when the
consumer unmounts, ideally by wiring removeEveAgent into an onUnmounted cleanup
near getOrCreateEveAgent/useEveAgent.

In `@layers/nuxi/app/composables/eve/session.ts`:
- Around line 10-23: regenerate() is replaying only the text from
lastUserMessageText, so any file parts attached to the last user prompt are
lost. Update the session flow in eve/session.ts so regeneration reuses the full
last user message contents, preserving both text and non-text parts instead of
filtering to text only. Use lastUserMessageText and the regenerate/send path
that rebuilds the prompt to locate the change, and make sure file-only and
text+file prompts are forwarded unchanged.

In `@layers/nuxi/app/pages/dashboard/chat/`[id].vue:
- Around line 83-86: The first-user-message branch in the chat page is
re-sending an already persisted message, which causes a duplicate user turn in
the database. Update the logic in the dashboard chat page to use
`chat.regenerate()` instead of `send({ parts })` when `isOwner.value` and the
single existing message is a user message, so the assistant response is
generated from existing history without calling `persistFirstUserMessage` again.
Refer to `chat.regenerate`, `send`, and `persistFirstUserMessage` in
`useAgentChat` to keep the fix aligned with the existing chat flow.

In `@layers/nuxi/server/api/internal/agent/rate-limit/consume.post.ts`:
- Around line 6-11: The consume.post handler is trusting a caller-supplied
userId, which lets an internal caller choose whose rate limit is decremented.
Update this route to derive the principal from the request context and use the
same server-side identity binding already used in
layers/nuxi/server/utils/rate-limit.ts, or verify any forwarded identity is
cryptographically bound before passing it to consumeAgentRateLimitForUser. Keep
the change localized to the consume.post handler and the related
identity-binding helper usage.

In `@layers/nuxi/server/api/internal/chats/`[id]/messages.post.ts:
- Around line 12-18: The `readValidatedBody` schema in `messages.post.ts` is too
permissive for `parts`, so tighten it to match the `UIMessage` contract instead
of using a loose object with only `type`. Update the `messages` validation to
use a strict discriminated union on `parts` (for example via
`z.discriminatedUnion('type', ...)`) with the valid part shapes, then remove the
unsafe `as UIMessage['parts']` cast where the validated payload is consumed.
Keep the fix localized to the `readValidatedBody` schema and the message
persistence path so downstream consumers like `generate-chat-title` and
`ChatContent` receive only well-formed parts.

In `@layers/nuxi/server/utils/search-github-issues.ts`:
- Around line 27-32: The default org query builder in searchGithubIssues is
generating an impossible GitHub search because the NUXT_ORGS qualifiers are
space-joined and therefore ANDed together. Update the query construction in
search-github-issues so the non-repo branch combines the org qualifiers with an
explicit OR and wraps them in parentheses, keeping the existing repo branch
unchanged.

In `@server/db/migrations/sqlite/meta/0006_snapshot.json`:
- Around line 267-340: The 0006 Drizzle snapshot metadata is stale and no longer
matches the real schema. Regenerate the snapshot for the SQLite migration so the
messages table reflects the composite primary key on chat_id and id, the
obsolete messages_chat_id_idx is removed, and the votes table foreign key uses
the composite chat_id/message_id to chat_id/id relationship. Update the snapshot
JSON in the 0006 snapshot file rather than hand-editing individual fields so the
schema state stays consistent for Drizzle migration diffing.

---

Minor comments:
In `@layers/nuxi/agent/hooks/chat-title.ts`:
- Around line 14-29: The chat title request in the `message.received` hook only
handles non-OK responses, so network exceptions from `fetch` can still bubble up
and break message flow. Wrap the entire title-generation block in a `try/catch`
in `chat-title.ts`, keeping the existing `response.ok` handling inside the `try`
and logging any thrown error as a best-effort failure. Use the existing
`appOrigin()`, `internalHeaders()`, and `console.warn` path so the hook degrades
gracefully without interrupting the main message lifecycle.

In `@layers/nuxi/agent/tools/search_github_issues.ts`:
- Around line 6-10: The search_github_issues tool currently allows blank or
whitespace-only queries because inputSchema uses a plain z.string() for query.
Update the query validator in search_github_issues.ts to require a trimmed,
non-empty string so the tool rejects empty searches before reaching GitHub. Use
the inputSchema definition and its query field as the place to apply the
constraint.

In `@layers/nuxi/agent/tools/show_blog_post.ts`:
- Around line 6-10: The title validation in showBlogPost is too permissive
because inputSchema accepts empty or whitespace-only strings. Update the zod
schema for title to trim the value and require at least one character before it
is sent to /api/internal/content, using the existing inputSchema in
show_blog_post.ts so the body builder only receives valid blog search terms.

In `@layers/nuxi/agent/tools/web_search.ts`:
- Around line 6-8: The web search tool schema currently accepts empty or
whitespace-only queries because the `inputSchema` in `web_search.ts` only uses
`z.string()`. Update the `query` field validation in `inputSchema` to require a
non-empty trimmed string by adding trimming and a minimum-length check so
invalid searches are rejected before reaching the remote search call.

In `@layers/nuxi/app/composables/useAgentChat.ts`:
- Around line 197-212: The first-user-message persistence flow in
persistFirstUserMessage can run twice because initialDbPersistDone.value is only
set after awaiting appendUserMessageToChat/createChatWithMessage. Set the flag
optimistically before the await so concurrent send/onSubmit or askQuestion calls
in useAgentChat cannot pass the guard twice, and if the persistence call fails,
roll the flag back so the message can be retried safely.
- Around line 14-19: `useAgentChat.ts` references `ChatEveState` in
`ChatModeOptions` without importing it explicitly, so add a type-only import for
`ChatEveState` from the shared chat types alongside the existing imports. Keep
the change localized to the `useAgentChat` composable and ensure the
`ChatModeOptions` definition resolves the type directly rather than relying on
auto-imports.

In `@layers/nuxi/server/api/chats/`[id]/state.patch.ts:
- Around line 54-63: The PATCH handler in state.patch.ts currently treats
body.messages as a full replacement but does not enforce that contract, so
partial payloads can delete existing chat history; either validate that the
caller always provides the complete snapshot in
persistChatState/createEveFinishHandler or change the update flow to merge
instead of replacing. Also update the schema.messages insert path to tolerate
duplicate message ids by using the existing tx.insert(schema.messages) flow with
conflict handling such as onConflictDoNothing, so a repeated id does not fail
the transaction.

In `@layers/nuxi/server/api/internal/agent/web-search.post.ts`:
- Around line 19-23: The web-search error handling in webSearch.post currently
returns the raw provider message to the client and assumes the thrown value is
always an Error. Update the catch block in the web-search handler to log the
full error server-side, including any cause chain, and return a generic
client-facing message instead of using error.message directly.

In `@layers/nuxi/server/api/internal/github/search-issues.post.ts`:
- Around line 6-10: The request validation in readValidatedBody for
search-issues.post.ts allows an empty query, which can lead to meaningless
searches in searchGitHubIssues. Update the z.object schema used in this handler
so the query field requires a non-empty string, matching the validation pattern
used by the sibling web-search endpoint and keeping input handling consistent.

In `@layers/nuxi/server/utils/internal-api.ts`:
- Around line 13-19: The bearer secret check in requireInternalRequest currently
uses a normal string comparison, which can leak timing information. Update the
authorization validation in this function to use a constant-time equality check
when comparing the incoming Authorization header against the expected Bearer
value, while preserving the existing 401 Unauthorized behavior for mismatches.

In `@nuxt.config.ts`:
- Around line 127-128: The runtimeConfig entry for internalApiSecret is
redundant because the internal API checks already read
process.env.INTERNAL_API_SECRET directly. Either remove internalApiSecret from
nuxt.config.ts if you want to keep the current env-based validation in
layers/nuxi/agent/lib/internal-api.ts and
layers/nuxi/server/utils/internal-api.ts, or refactor those internal-api helpers
to read useRuntimeConfig().internalApiSecret and drop the fallback so missing
secrets fail consistently.

In `@server/mcp/prompts/admin/review-agent-chats.ts`:
- Line 22: The chat link in the admin review prompt uses a non-existent
`/admin/agent/` route, so update the template in review-agent-chats.ts to match
the real chat page route used by the app. Change the generated link text for
“Worst sessions” to point to the dashboard chat path, using the same chat id
placeholder, so reports align with the route handled by the chat page component.

---

Nitpick comments:
In `@layers/nuxi/app/composables/useChatTools.ts`:
- Around line 85-95: `getToolText` is normalizing the tool name before calling
`parseToolName`, but `parseToolName` also normalizes internally, causing
redundant double normalization. Update the flow so normalization happens in only
one place: either pass the raw tool name into `parseToolName` and keep its
current behavior, or remove the internal normalization and let `getToolText` own
it. Use the `getToolText` and `parseToolName` symbols to keep the change
localized and consistent.

In `@layers/nuxi/README.md`:
- Line 5: The README’s fenced directory tree block is triggering a markdownlint
MD040 false positive because it has no language tag. Update the fenced block in
the README to use a non-code label such as text so the directory tree remains
rendered as plain text while suppressing the warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5af94e76-a600-45a3-a7ea-8ece60c880b3

📥 Commits

Reviewing files that changed from the base of the PR and between d0ce856 and f5b17b2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (84)
  • .env.example
  • .github/workflows/ci.yml
  • .gitignore
  • README.md
  • app/composables/useNavigation.ts
  • layers/nuxi/README.md
  • layers/nuxi/agent/agent.ts
  • layers/nuxi/agent/channels/eve.ts
  • layers/nuxi/agent/channels/slack.ts
  • layers/nuxi/agent/connections/nuxt-mcp.ts
  • layers/nuxi/agent/hooks/chat-title.ts
  • layers/nuxi/agent/hooks/rate-limit.ts
  • layers/nuxi/agent/instructions.ts
  • layers/nuxi/agent/lib/base-instructions.ts
  • layers/nuxi/agent/lib/define-nuxt-tool.ts
  • layers/nuxi/agent/lib/internal-api.ts
  • layers/nuxi/agent/tools/admin-mcp.ts
  • layers/nuxi/agent/tools/open_playground.ts
  • layers/nuxi/agent/tools/report_issue.ts
  • layers/nuxi/agent/tools/search_github_issues.ts
  • layers/nuxi/agent/tools/show_blog_post.ts
  • layers/nuxi/agent/tools/show_hosting.ts
  • layers/nuxi/agent/tools/show_module.ts
  • layers/nuxi/agent/tools/show_template.ts
  • layers/nuxi/agent/tools/web_search.ts
  • layers/nuxi/app/components/agent/AgentChatMessages.vue
  • layers/nuxi/app/components/agent/AgentChatPrompt.vue
  • layers/nuxi/app/components/agent/AgentPanelChat.vue
  • layers/nuxi/app/components/chat/ChatContent.vue
  • layers/nuxi/app/composables/eve/adapter.ts
  • layers/nuxi/app/composables/eve/init.ts
  • layers/nuxi/app/composables/eve/session.ts
  • layers/nuxi/app/composables/eve/thread-state.ts
  • layers/nuxi/app/composables/eve/types.ts
  • layers/nuxi/app/composables/useAgentChat.ts
  • layers/nuxi/app/composables/useChatTools.ts
  • layers/nuxi/app/composables/useChatVotes.ts
  • layers/nuxi/app/composables/usePasteAttachment.ts
  • layers/nuxi/app/pages/dashboard/chat/[id].vue
  • layers/nuxi/nuxt.config.ts
  • layers/nuxi/package.json
  • layers/nuxi/server/api/chats/[id].post.ts
  • layers/nuxi/server/api/chats/[id]/messages.delete.ts
  • layers/nuxi/server/api/chats/[id]/messages.post.ts
  • layers/nuxi/server/api/chats/[id]/state.patch.ts
  • layers/nuxi/server/api/chats/index.post.ts
  • layers/nuxi/server/api/internal/agent/rate-limit/consume.post.ts
  • layers/nuxi/server/api/internal/agent/web-search.post.ts
  • layers/nuxi/server/api/internal/chats/[id]/context.get.ts
  • layers/nuxi/server/api/internal/chats/[id]/messages.post.ts
  • layers/nuxi/server/api/internal/chats/[id]/title.post.ts
  • layers/nuxi/server/api/internal/content.post.ts
  • layers/nuxi/server/api/internal/github/search-issues.post.ts
  • layers/nuxi/server/api/internal/session.get.ts
  • layers/nuxi/server/db/schema.ts
  • layers/nuxi/server/utils/chat-messages.ts
  • layers/nuxi/server/utils/generate-chat-title.ts
  • layers/nuxi/server/utils/internal-api.ts
  • layers/nuxi/server/utils/rate-limit.ts
  • layers/nuxi/server/utils/search-github-issues.ts
  • layers/nuxi/server/utils/stats.ts
  • layers/nuxi/server/utils/tools/search-github-issues.ts
  • layers/nuxi/server/utils/tools/show-blog-post.ts
  • layers/nuxi/server/utils/tools/show-hosting.ts
  • layers/nuxi/server/utils/tools/show-template.ts
  • layers/nuxi/shared/types/chat.ts
  • layers/nuxi/shared/utils/chat.ts
  • nuxt.config.ts
  • package.json
  • server/db/migrations/sqlite/0005_chat_eve_state.sql
  • server/db/migrations/sqlite/0006_drop_agent_usage_metrics.sql
  • server/db/migrations/sqlite/meta/0006_snapshot.json
  • server/db/migrations/sqlite/meta/_journal.json
  • server/mcp/admin.ts
  • server/mcp/prompts/admin/review-agent-chats.ts
  • server/mcp/prompts/admin/weekly-digest.ts
  • server/mcp/tools/admin/agent-usage-stats.ts
  • server/mcp/tools/admin/get-agent-chat.ts
  • server/mcp/tools/admin/list-agent-chats.ts
  • server/mcp/tools/admin/list-agent-votes.ts
  • server/mcp/tools/modules/list-modules.ts
  • test/nuxt/setup.ts
  • vercel.json
  • vitest.config.ts
💤 Files with no reviewable changes (6)
  • layers/nuxi/server/utils/tools/show-blog-post.ts
  • layers/nuxi/server/api/chats/[id].post.ts
  • layers/nuxi/server/utils/tools/show-hosting.ts
  • layers/nuxi/server/utils/tools/search-github-issues.ts
  • layers/nuxi/server/utils/stats.ts
  • layers/nuxi/server/utils/tools/show-template.ts

Comment thread layers/nuxi/agent/channels/eve.ts
Comment thread layers/nuxi/agent/lib/internal-api.ts
Comment thread layers/nuxi/agent/tools/admin-mcp.ts
Comment thread layers/nuxi/agent/tools/show_module.ts Outdated
Comment thread layers/nuxi/app/composables/eve/init.ts Outdated
Comment thread layers/nuxi/app/pages/dashboard/chat/[id].vue
Comment thread layers/nuxi/server/api/internal/agent/rate-limit/consume.post.ts Outdated
Comment thread layers/nuxi/server/api/internal/chats/[id]/messages.post.ts Outdated
Comment thread layers/nuxi/server/utils/search-github-issues.ts
Comment thread server/db/migrations/sqlite/meta/0006_snapshot.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@layers/nuxi/server/utils/internal-api.ts`:
- Around line 30-33: `resolveInternalPrincipalId` currently falls back to
`session.id`, which is not a stable principal identifier and can break anonymous
ownership when the session rotates. Add a persistent `anonymousUserId` to the
session payload when the session is first created, preserve it through
regeneration/update flows, and change `resolveInternalPrincipalId` to return
`session.user?.id` or this stable `anonymousUserId` instead of the token ID.
Then update the ownership/rate-limit checks in handlers like
`agent/feedback.post.ts` and `chats/[id].get.ts` to compare against the same
stable anonymous identifier.

In `@layers/nuxi/server/utils/message-parts.ts`:
- Around line 30-38: The uiMessagePartSchema in message-parts.ts is missing
valid part types that the app already handles, so message persistence fails for
tool and source entries. Update uiMessagePartSchema to accept tool-prefixed
parts (matching type values starting with tool-) and add explicit support for
source-url and source-document parts, keeping uiMessagePartsSchema aligned with
the persisted message shapes. Use the existing textPartSchema, filePartSchema,
reasoningPartSchema, dynamicToolPartSchema, and stepStartPartSchema as the
reference point and extend the union with new schemas or custom validators so
these parts round-trip through messages.post and state.patch without Zod errors.

In `@package.json`:
- Line 54: The ai dependency range is broader than the minimumReleaseAge
exclusion, so updates like 7.0.3 can slip in and fail fresh installs. In
package.json, either pin the ai dependency to 7.0.2 or, if patch updates are
intended, update the minimumReleaseAgeExclude entry in pnpm-workspace.yaml to
cover the allowed ai 7.0.x versions. Use the ai dependency entry and the
minimumReleaseAgeExclude configuration as the matching symbols to keep both
policies aligned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6208f5a4-bd02-4124-893d-e8fe076907ca

📥 Commits

Reviewing files that changed from the base of the PR and between f5b17b2 and 625c74d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • .env.example
  • layers/nuxi/README.md
  • layers/nuxi/agent/channels/eve.ts
  • layers/nuxi/agent/hooks/chat-title.ts
  • layers/nuxi/agent/hooks/rate-limit.ts
  • layers/nuxi/agent/lib/internal-api.ts
  • layers/nuxi/agent/tools/admin-mcp.ts
  • layers/nuxi/agent/tools/search_github_issues.ts
  • layers/nuxi/agent/tools/show_blog_post.ts
  • layers/nuxi/agent/tools/show_module.ts
  • layers/nuxi/agent/tools/web_search.ts
  • layers/nuxi/app/composables/eve/init.ts
  • layers/nuxi/app/composables/eve/session.ts
  • layers/nuxi/app/composables/useAgentChat.ts
  • layers/nuxi/app/composables/useChatTools.ts
  • layers/nuxi/app/pages/dashboard/chat/[id].vue
  • layers/nuxi/server/api/chats/[id]/state.patch.ts
  • layers/nuxi/server/api/internal/agent/rate-limit/consume.post.ts
  • layers/nuxi/server/api/internal/agent/web-search.post.ts
  • layers/nuxi/server/api/internal/chats/[id]/context.get.ts
  • layers/nuxi/server/api/internal/chats/[id]/messages.post.ts
  • layers/nuxi/server/api/internal/github/search-issues.post.ts
  • layers/nuxi/server/utils/internal-api.ts
  • layers/nuxi/server/utils/message-parts.ts
  • layers/nuxi/server/utils/search-github-issues.ts
  • nuxt.config.ts
  • package.json
  • pnpm-workspace.yaml
  • server/db/migrations/sqlite/meta/0006_snapshot.json
  • server/mcp/prompts/admin/review-agent-chats.ts
✅ Files skipped from review due to trivial changes (2)
  • layers/nuxi/README.md
  • server/db/migrations/sqlite/meta/0006_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (18)
  • layers/nuxi/agent/tools/search_github_issues.ts
  • layers/nuxi/agent/tools/web_search.ts
  • layers/nuxi/agent/tools/show_blog_post.ts
  • .env.example
  • layers/nuxi/agent/hooks/chat-title.ts
  • server/mcp/prompts/admin/review-agent-chats.ts
  • layers/nuxi/server/utils/search-github-issues.ts
  • layers/nuxi/app/composables/eve/init.ts
  • layers/nuxi/app/composables/eve/session.ts
  • layers/nuxi/server/api/internal/github/search-issues.post.ts
  • layers/nuxi/agent/tools/show_module.ts
  • layers/nuxi/server/api/chats/[id]/state.patch.ts
  • layers/nuxi/agent/lib/internal-api.ts
  • layers/nuxi/app/composables/useChatTools.ts
  • layers/nuxi/agent/hooks/rate-limit.ts
  • layers/nuxi/agent/channels/eve.ts
  • layers/nuxi/server/api/internal/chats/[id]/context.get.ts
  • layers/nuxi/agent/tools/admin-mcp.ts

Comment thread layers/nuxi/server/utils/internal-api.ts Outdated
Comment thread layers/nuxi/server/utils/message-parts.ts Outdated
Comment thread package.json
HugoRCD added 2 commits June 26, 2026 14:04
Use persistent anonymousUserId instead of rotating session.id for chat ownership and rate limits, and accept tool-*, source-url, and source-document parts in persistence validation.
Chats require login via requireUserSession; remove unused internal messages route and session-principal indirection. Derive DB persist mode from initialMessages in useAgentChat.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
layers/nuxi/app/composables/useAgentChat.ts (1)

197-215: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Serialize the first-message persistence.

Line 199 marks the DB bootstrap as done before appendUserMessageToChat / createChatWithMessage has resolved. A second send() in that window skips persistence and can reach Eve before the chat row exists, which risks dropping or mis-ordering the opening messages.

Suggested fix
+ let initialDbPersistPromise: Promise<void> | null = null
+
  async function persistFirstUserMessage(parts: UIMessage['parts']) {
    if (initialDbPersistDone.value) return
-   initialDbPersistDone.value = true
+   if (initialDbPersistPromise) {
+     await initialDbPersistPromise
+     return
+   }

    const metadata = {
      createdAt: new Date().toISOString(),
      ...(useContext.value && agent.currentPage.value ? { pagePath: agent.currentPage.value } : {})
    }

-   try {
-     if (chatOptions.initialMessages?.length) {
-       await appendUserMessageToChat(chatOptions.chatId, parts, metadata)
-     } else {
-       await createChatWithMessage(chatOptions.chatId, parts, metadata)
-     }
-   } catch (error) {
-     initialDbPersistDone.value = false
-     throw error
-   }
+   initialDbPersistPromise = (async () => {
+     if (chatOptions.initialMessages?.length) {
+       await appendUserMessageToChat(chatOptions.chatId, parts, metadata)
+     } else {
+       await createChatWithMessage(chatOptions.chatId, parts, metadata)
+     }
+     initialDbPersistDone.value = true
+   })()
+
+   try {
+     await initialDbPersistPromise
+   } finally {
+     initialDbPersistPromise = null
+   }
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layers/nuxi/app/composables/useAgentChat.ts` around lines 197 - 215,
Serialize the first-message persistence in persistFirstUserMessage so
initialDbPersistDone.value is not set to true until appendUserMessageToChat or
createChatWithMessage has successfully completed. If a second send() can arrive
while the first call is still awaiting the DB write, make it wait or queue
behind the same in-flight promise so the chat row is created before any later
message is sent to Eve, and keep the rollback path in the catch block for
failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@layers/nuxi/app/composables/useNuxtAgent.ts`:
- Around line 106-108: The shared usage state in useNuxtAgent is not refreshed
when the auth principal changes, so stale counters can remain after
sign-in/sign-out. Update the existing watch(loggedIn) logic in useNuxtAgent to
also call refreshUsage alongside the chat refresh whenever the principal flips,
keeping rateLimitReached and usage in sync with the current user.

In `@layers/nuxi/server/utils/message-parts.ts`:
- Around line 30-33: The source-url schema in sourceUrlPartSchema is currently
too permissive because url accepts any string, so tighten it to only allow safe
web links with explicit http: or https: protocols. Update the validation in
uiMessagePartsSchema’s source-url branch to enforce the protocol check at the
schema level, keeping the existing type shape intact while rejecting
javascript:, data:, and other non-web schemes.

---

Outside diff comments:
In `@layers/nuxi/app/composables/useAgentChat.ts`:
- Around line 197-215: Serialize the first-message persistence in
persistFirstUserMessage so initialDbPersistDone.value is not set to true until
appendUserMessageToChat or createChatWithMessage has successfully completed. If
a second send() can arrive while the first call is still awaiting the DB write,
make it wait or queue behind the same in-flight promise so the chat row is
created before any later message is sent to Eve, and keep the rollback path in
the catch block for failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a20391d7-7c34-4618-99f0-479e0ce4bea8

📥 Commits

Reviewing files that changed from the base of the PR and between 625c74d and 053dc99.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • layers/nuxi/app/components/agent/AgentChatMessages.vue
  • layers/nuxi/app/composables/eve/adapter.ts
  • layers/nuxi/app/composables/useAgentChat.ts
  • layers/nuxi/app/composables/useNuxtAgent.ts
  • layers/nuxi/app/pages/dashboard/chat/[id].vue
  • layers/nuxi/package.json
  • layers/nuxi/server/api/agent/feedback.post.ts
  • layers/nuxi/server/api/chats/[id].delete.ts
  • layers/nuxi/server/api/chats/[id].get.ts
  • layers/nuxi/server/api/chats/[id]/branch.post.ts
  • layers/nuxi/server/api/chats/[id]/messages.delete.ts
  • layers/nuxi/server/api/chats/[id]/title.patch.ts
  • layers/nuxi/server/api/chats/[id]/visibility.patch.ts
  • layers/nuxi/server/api/chats/[id]/votes.get.ts
  • layers/nuxi/server/api/chats/[id]/votes.post.ts
  • layers/nuxi/server/api/chats/index.get.ts
  • layers/nuxi/server/api/internal/agent/rate-limit/consume.post.ts
  • layers/nuxi/server/api/internal/chats/[id]/context.get.ts
  • layers/nuxi/server/api/internal/session.get.ts
  • layers/nuxi/server/utils/internal-api.ts
  • layers/nuxi/server/utils/message-parts.ts
  • layers/nuxi/server/utils/rate-limit.ts
  • pnpm-workspace.yaml
  • shared/types/auth.d.ts
💤 Files with no reviewable changes (2)
  • layers/nuxi/app/pages/dashboard/chat/[id].vue
  • layers/nuxi/server/utils/internal-api.ts
✅ Files skipped from review due to trivial changes (2)
  • layers/nuxi/package.json
  • pnpm-workspace.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • layers/nuxi/server/api/internal/chats/[id]/context.get.ts
  • layers/nuxi/server/api/internal/session.get.ts

Comment thread layers/nuxi/app/composables/useNuxtAgent.ts
Comment thread layers/nuxi/server/utils/message-parts.ts
Reload rate-limit counters when the session principal changes, and restrict persisted source-url parts to http(s) links.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant